//
//  YTXGeometry.cpp
//  ChartRefactor
//
//  Created by CaoJun on 15/3/2.
//  Copyright (c) 2015年 CaoJun. All rights reserved.
//

#include "YTXGeometry.h"

using namespace ytx;

YTXPoint::YTXPoint(void) : x(0), y(0)
{
}

YTXPoint::YTXPoint(float x, float y) : x(x), y(y)
{
}

YTXPoint::YTXPoint(const YTXPoint& other) : x(other.x), y(other.y)
{
}

YTXPoint::YTXPoint(const YTXSize& size) : x(size.width), y(size.height)
{
}

YTXPoint& YTXPoint::operator= (const YTXPoint& other)
{
    setPoint(other.x, other.y);
    return *this;
}

YTXPoint& YTXPoint::operator= (const YTXSize& size)
{
    setPoint(size.width, size.height);
    return *this;
}

YTXPoint YTXPoint::operator+(const YTXPoint& right) const
{
    return YTXPoint(this->x + right.x, this->y + right.y);
}

YTXPoint YTXPoint::operator-(const YTXPoint& right) const
{
    return YTXPoint(this->x - right.x, this->y - right.y);
}

YTXPoint YTXPoint::operator-() const
{
    return YTXPoint(-x, -y);
}

YTXPoint YTXPoint::operator*(float a) const
{
    return YTXPoint(this->x * a, this->y * a);
}

YTXPoint YTXPoint::operator/(float a) const
{
    return YTXPoint(this->x / a, this->y / a);
}

void YTXPoint::setPoint(float x, float y)
{
    this->x = x;
    this->y = y;
}

bool YTXPoint::equals(const YTXPoint& target) const
{
    return (fabs(this->x - target.x) < FLT_EPSILON)
    && (fabs(this->y - target.y) < FLT_EPSILON);
}

bool YTXPoint::fuzzyEquals(const YTXPoint& b, float var) const
{
    if(x - var <= b.x && b.x <= x + var)
        if(y - var <= b.y && b.y <= y + var)
            return true;
    return false;
}

float YTXPoint::getAngle(const YTXPoint& other) const
{
    YTXPoint a2 = normalize();
    YTXPoint b2 = other.normalize();
    float angle = atan2f(a2.cross(b2), a2.dot(b2));
    if( fabs(angle) < FLT_EPSILON ) return 0.f;
    return angle;
}

YTXPoint YTXPoint::rotateByAngle(const YTXPoint& pivot, float angle) const
{
    return pivot + (*this - pivot).rotate(YTXPoint::forAngle(angle));
}

// implementation of YTXSize

YTXSize::YTXSize(void) : width(0), height(0)
{
}

YTXSize::YTXSize(float width, float height) : width(width), height(height)
{
}

YTXSize::YTXSize(const YTXSize& other) : width(other.width), height(other.height)
{
}

YTXSize::YTXSize(const YTXPoint& point) : width(point.x), height(point.y)
{
}

YTXSize& YTXSize::operator= (const YTXSize& other)
{
    setSize(other.width, other.height);
    return *this;
}

YTXSize& YTXSize::operator= (const YTXPoint& point)
{
    setSize(point.x, point.y);
    return *this;
}

YTXSize YTXSize::operator+(const YTXSize& right) const
{
    return YTXSize(this->width + right.width, this->height + right.height);
}

YTXSize YTXSize::operator-(const YTXSize& right) const
{
    return YTXSize(this->width - right.width, this->height - right.height);
}

YTXSize YTXSize::operator*(float a) const
{
    return YTXSize(this->width * a, this->height * a);
}

YTXSize YTXSize::operator/(float a) const
{
    return YTXSize(this->width / a, this->height / a);
}

void YTXSize::setSize(float width, float height)
{
    this->width = width;
    this->height = height;
}

bool YTXSize::equals(const YTXSize& target) const
{
    return (fabs(this->width  - target.width)  < FLT_EPSILON)
    && (fabs(this->height - target.height) < FLT_EPSILON);
}

// implementation of YTXRect

YTXRect::YTXRect(void)
{
    setRect(0.0f, 0.0f, 0.0f, 0.0f);
}

YTXRect::YTXRect(float x, float y, float width, float height)
{
    setRect(x, y, width, height);
}

YTXRect::YTXRect(const YTXRect& other)
{
    setRect(other.origin.x, other.origin.y, other.size.width, other.size.height);
}

YTXRect& YTXRect::operator= (const YTXRect& other)
{
    setRect(other.origin.x, other.origin.y, other.size.width, other.size.height);
    return *this;
}

void YTXRect::setRect(float x, float y, float width, float height)
{
    // CGRect can support width<0 or height<0

    origin.x = x;
    origin.y = y;

    size.width = width;
    size.height = height;
}

bool YTXRect::equals(const YTXRect& rect) const
{
    return (origin.equals(rect.origin) &&
            size.equals(rect.size));
}

float YTXRect::getMaxX() const
{
    return (float)(origin.x + size.width);
}

float YTXRect::getMidX() const
{
    return (float)(origin.x + size.width / 2.0);
}

float YTXRect::getMinX() const
{
    return origin.x;
}

float YTXRect::getMaxY() const
{
    return origin.y + size.height;
}

float YTXRect::getMidY() const
{
    return (float)(origin.y + size.height / 2.0);
}

float YTXRect::getMinY() const
{
    return origin.y;
}

bool YTXRect::containsPoint(const YTXPoint& point) const
{
    bool bRet = false;

    if (point.x >= getMinX() && point.x <= getMaxX()
        && point.y >= getMinY() && point.y <= getMaxY())
    {
        bRet = true;
    }

    return bRet;
}

bool YTXRect::intersectsRect(const YTXRect& rect) const
{
    return !(     getMaxX() < rect.getMinX() ||
             rect.getMaxX() <      getMinX() ||
             getMaxY() < rect.getMinY() ||
             rect.getMaxY() <      getMinY());
}
